﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Microsoft.SharePoint;
using System.Windows.Forms;
using Microsoft.SharePoint.Administration;
using System.IO;
using System.Data;

namespace SharePointUsers
{
    class SPUmanager
    {
        public SPUmanager()
        {
        }
        public SPUmanager(bool includeSiteCollections, bool includeSubSites)
        {
            _IncludeSiteCollections = includeSiteCollections;
            _IncludeSubSites = includeSubSites;
        }
        public SPUmanager(bool includeSiteCollections, bool includeSubSites, bool verbose)
        {
            _IncludeSiteCollections = includeSiteCollections;
            _IncludeSubSites = includeSubSites;
            _Verbose = verbose;
        }
        #region Private Members
        bool _IncludeSiteCollections = false;
        bool _IncludeSubSites = false;
        bool _Verbose = false;
        Hashtable _Users = new Hashtable();
        #endregion
        #region Properties
        public Hashtable Users
        {
            get
            {
                return _Users;
            }
        }
        #endregion
        private bool GetUsers(string url)
        {
            int SiteCount = 0;
            string ErrorMessage = "Error connecting to the local SharePoint farm." +
                                  " The farm is not available or you don't have enough permissions to access the farm.\n";

            SPWebService CurrentFarm = SPWebService.ContentService;
            if (CurrentFarm == null)
                WriteError("Unable to connect to SharePoint");
            else
            {
                try
                {
                    if (string.IsNullOrEmpty(url))
                    {
                        foreach (SPWebApplication wa in CurrentFarm.WebApplications)
                        {
                            foreach (SPSite sc in wa.Sites)
                            {
                                SiteCount++;
                                string Error = "";
                                try
                                {
                                    //Test the site access
                                    DateTime LastModified = sc.RootWeb.LastItemModifiedDate;
                                    GetSiteUsers(sc.RootWeb, false, false);
                                    if (_IncludeSubSites)
                                    {
                                        string WebTitle = "";
                                        string WebUrl = "";
                                        try
                                        {
                                            using (SPWeb web = sc.OpenWeb())
                                            {
                                                WebTitle = web.Title;
                                                WebUrl = web.Url;
                                                GetWebUsers(web, null);
                                            }
                                        }
                                        catch (Exception Ex)
                                        {
                                            if (_Verbose)
                                            {
                                                Error = "[Warning]: Unable to open the site '{0}' @ '{1}'." +
                                                        " Make sure you have access to that site before you run the tool.\nError Details:\n" + Ex.Message;
                                            }
                                            else
                                            {
                                                Error = "[Warning]: Unable to open the site '{0}' @ '{1}'.";     
                                            }
                                            WriteWarning(Error, WebTitle, WebUrl);
                                        }
                                    }
                                }
                                catch (Exception Ex)
                                {
                                    if (_Verbose)
                                    {
                                        Error = "[Warning]: " + Ex.Message + "\nStack Trace:\n"+ Ex.StackTrace;
                                    }
                                    else
                                    {
                                        Error = "[Warning]: " + Ex.Message;
                                    }
                                    WriteWarning(Error);
                                }
                                sc.Dispose();

                            }
                        }
                    }
                    else
                    {
                       ErrorMessage = "Error connecting to the specified SharePoint application Url." +
                                 " The farm is not available or you don't have enough permissions to access the application.\n";
                        using (SPSite site = new SPSite(url))
                        {
                            SPWebApplication wa = site.WebApplication;
                            foreach (SPSite sc in wa.Sites)
                            {

                                SiteCount++;
                                string Error = "";
                                try
                                {
                                    //Test the site access
                                    DateTime LastModified = sc.RootWeb.LastItemModifiedDate;
                                    GetSiteUsers(sc.RootWeb, false, false);
                                    if (_IncludeSubSites)
                                    {
                                        string WebTitle = "";
                                        string WebUrl = "";
                                        try
                                        {
                                            using (SPWeb web = sc.OpenWeb())
                                            {
                                                WebTitle = web.Title;
                                                WebUrl = web.Url;
                                                GetWebUsers(web, null);
                                            }
                                        }
                                        catch (Exception Ex)
                                        {
                                            if (_Verbose)
                                            {
                                                Error = "[Warning]: Unable to open the site '{0}' @ '{1}'." +
                                                        " Make sure you have access to that site before you run the tool.\nError Details:\n" + Ex.Message + "\nStack Trace:\n" +
                                                        Ex.StackTrace;
                                            }
                                            else
                                            {
                                                Error = "[Warning]: Unable to open the site '{0}' @ '{1}'." +
                                                        " Make sure you have access to that site before you run the tool.\nError Details:\n" + Ex.Message;
                                            }
                                            WriteWarning(Error, WebTitle, WebUrl);
                                        }
                                    }
                                }
                                catch (Exception Ex)
                                {
                                    if (_Verbose)
                                    {
                                        Error = "[Warning]: " + Ex.Message + "\nStack Trace:\n" + Ex.StackTrace;
                                    }
                                    else
                                    {
                                        Error = "[Warning]: " + Ex.Message;
                                    }
                                    WriteWarning(Error);
                                }
                                if (sc != null)
                                    sc.Dispose();
                            }
                        }
                    }
                }
                catch (Exception Ex)
                {
                    if (_Verbose)
                    {
                        WriteError(ErrorMessage + string.Format("Error details:{0}, Call stack: {1}", Ex.Message, Ex.StackTrace));
                    }
                    else
                    {
                        WriteError(ErrorMessage + string.Format("Error details:{0}", Ex.Message));
                    }
                    return false;
                }
                WriteSuccess("{0} Site Collection(s) loaded successfully", SiteCount);
            }
            return true;
        }
        public bool GetFarmUsers()
        {
            return GetUsers("");
        }
        public bool GetWebAppUsers(string url)
        {
            return GetUsers(url);
        }
        public void GetWebUsers(SPWeb web, HashSet<string> ProcessedGroup)
        {
            HashSet<string> ProcessedUserList = new HashSet<string>();
            if (ProcessedGroup == null)
                ProcessedGroup = new HashSet<string>();
            string WebUrl = web.Url;
            string SiteUrl = web.Site.Url;
            //Get the users in the users list
            foreach (SPUser user in web.Users)
            {
                AddWebData(user, _Users, WebUrl, SiteUrl);
                ProcessedUserList.Add(user.LoginName);
            }

            //Get the users with group assignments
            foreach (SPGroup group in web.Groups)
            {
                if (!ProcessedGroup.Contains(group.Name))
                {
                    foreach (SPUser user in group.Users)
                    {
                        if (!ProcessedUserList.Contains(user.LoginName))
                        {
                            AddWebData(user, _Users, WebUrl, SiteUrl);
                            ProcessedUserList.Add(user.LoginName);
                        }
                    }
                    ProcessedGroup.Add(group.Name);
                }
            }

            //Scan subsites
            foreach (SPWeb Subweb in web.Webs)
            {
                try
                {
                    GetWebUsers(Subweb, ProcessedGroup);
                }
                finally
                {
                    if (Subweb != null)
                        Subweb.Dispose();
                }
            }
        }
        private void AddWebData(SPUser user, Hashtable Users, string WebUrl, string SiteUrl)
        {
            string LoginName = user.LoginName.ToUpper();
            UserInfoObj userData = Users[LoginName] as UserInfoObj;
            if (userData == null)
            {
                if(_Verbose)
                    Console.WriteLine("Skipped Account: "  + LoginName);
                return;
            }
            userData.Webs.Add(SiteUrl.ToLower() + ";" + WebUrl.ToLower());
            Users[LoginName] = userData;
        }
        public Hashtable GetSiteUsers(SPWeb CurrentWeb, bool ActiveOnly,
                              bool ExcludeDeleted)
        {
            SPQuery UsersQuery = new SPQuery();
            //Build the user CAML Query
            string IsActiveCondition = "<Eq><FieldRef ID='IsActive'/>" +
                                       "<Value Type='Boolean'>1</Value></Eq>";
            string IsNotDeletedCondition = "<Eq><FieldRef Name='Deleted'/>" +
                                       "<Value Type='Boolean'>0</Value></Eq>";
            if (!ExcludeDeleted) IsNotDeletedCondition = "";
            UsersQuery.Query = string.Concat(
                           "<Where>",
                              "<Eq><FieldRef Name='ContentType'/>",
                              "<Value Type='TEXT'>Person</Value></Eq>",
                //IsActiveCondition,
                              IsNotDeletedCondition,
                           "</Where>",
                           "<OrderBy>",
                              "<FieldRef Name='Name' Ascending='TRUE' />",
                           "</OrderBy>");

            //UsersQuery.ViewFields = string.Concat(
            //                    "<FieldRef Name='Name' /><FieldRef Name='IsActive' /><FieldRef Name='EMail' /><FieldRef Name='Account' />");

            //UsersQuery.ViewFieldsOnly = true; // Fetch only the data that we need.

            SPListItemCollection UsersResult = CurrentWeb.Site.RootWeb.SiteUserInfoList.GetItems(UsersQuery);
            string SiteUrl = CurrentWeb.Site.Url;


            foreach (SPListItem user in UsersResult)
            {
                //if (ActiveOnly && user["Is Active"].ToString() == "False")
                //   continue;
                //Users.Add(user["Account"].ToString().ToUpper());
                AddUserInfoCheckUnique(user, _Users, SiteUrl);
            }
            
            return _Users;
        }
        public bool AddUserInfoCheckUnique(SPListItem user, Hashtable Users, string SiteUrl)
        {
            string UserLoginName = user["Account"].ToString().ToUpper();
            if (UserLoginName != null && !Users.Contains(UserLoginName))
            {
                UserInfoObj userData = new UserInfoObj();
                userData.LoginName = UserLoginName;
                userData.DisplayName = user.Title;
                userData.Sites = new HashSet<string>() { SiteUrl };
                object Email = user["EMail"];
                if (Email != null)
                    userData.Email = Email.ToString();
                Users.Add(UserLoginName, userData);
                return true;
            }
            else
            {
                UserInfoObj userData = Users[UserLoginName] as UserInfoObj;
                userData.Sites.Add(SiteUrl);
                Users[UserLoginName] = userData;
            }
            return false;
        }
        public void AddUserInfo(SPListItem user, Hashtable UsersList)
        {
            string UserLoginName = user["Account"].ToString().ToUpper();
            UserInfoObj userData = new UserInfoObj();
            userData.LoginName = UserLoginName;
            userData.DisplayName = user.Title;
            userData.Email = user["EMail"].ToString();
            UsersList.Add(UserLoginName, userData);
        }
        public void RemoveSystemAccounts(Hashtable UsersList)
        {
            string SystemAccount = "System Acconut";

        }
        public void WriteUserDataToCSVFile(string fileName)
        {
            WriteUserDataToFile(fileName, _IncludeSiteCollections, _IncludeSubSites);
        }
        public void WriteUserDataToCSVFile(string fileName, bool includeSiteCollections, bool includeSubSites)
        {
            WriteUserDataToFile(fileName, includeSiteCollections, includeSubSites);
        }
        private void WriteUserDataToFile(string fileName, bool IncludeSiteCollections, bool IncludeSubSites)
        {
            StreamWriter SR = null;
            try
            {
                SR = new StreamWriter(fileName);
                if (IncludeSiteCollections && !IncludeSubSites)
                {
                    SR.WriteLine("DisplayName,UserLogin,Email,Sites");
                    foreach (object user in _Users.Values)
                    {
                        UserInfoObj userObj = user as UserInfoObj;
                        foreach (string site in userObj.Sites)
                        {
                            SR.WriteLine("\"{0}\",{1},{2},{3}", userObj.DisplayName, userObj.LoginName, userObj.Email, site);
                        }
                    }
                }
                else if (IncludeSubSites)
                {
                    SR.WriteLine("DisplayName,UserLogin,Email,Sites,SubSites");
                    foreach (object user in _Users.Values)
                    {
                        UserInfoObj userObj = user as UserInfoObj;
                        foreach (string SiteWebInfo in userObj.Webs)
                        {
                            string[] Urls = SiteWebInfo.Split(';');
                            SR.WriteLine("\"{0}\",{1},{2},{3},{4}", userObj.DisplayName, userObj.LoginName, userObj.Email, Urls[0], Urls[1]);
                        }
                    }
                }
                else
                {
                    SR.WriteLine("DisplayName,UserLogin,Email");
                    foreach (object user in _Users.Values)
                    {
                        UserInfoObj userObj = user as UserInfoObj;
                        SR.WriteLine("\"{0}\",{1},{2}", userObj.DisplayName, userObj.LoginName, userObj.Email);
                    }
                }

            }
            catch (Exception)
            {
                if (SR != null)
                {
                    SR.Flush();
                    SR.Close();
                }
                throw;
            }
            if (SR != null)
            {
                SR.Flush();
                SR.Close();
            }
            
        }
        public static void WriteDataTableToFile(string fileName, DataTable Data )
        {
            StreamWriter SR = null;
            try
            {
                SR = new StreamWriter(fileName);
                //Build the column headers
                StringBuilder Headers = new StringBuilder();

                for (int i = 0; i < Data.Columns.Count; i++)
                {
                    if (i != (Data.Columns.Count - 1))
                        Headers.AppendFormat("{0},", Data.Columns[i].ColumnName);
                    else
                        Headers.AppendFormat("{0}", Data.Columns[i].ColumnName);

                }
                SR.WriteLine(Headers.ToString());
                foreach (DataRow site in Data.Rows)
                {
                    StringBuilder Record = new StringBuilder();
                         
                    for (int i = 0; i < Data.Columns.Count; i++)
                    {
                        if(i != (Data.Columns.Count -1))
                            Record.AppendFormat("\"{0}\",", site[i]);
                        else
                            Record.AppendFormat("\"{0}\"", site[i]);

                    }
                    SR.WriteLine(Record.ToString());
                }


            }
            catch (Exception)
            {
                if (SR != null)
                {
                    SR.Flush();
                    SR.Close();
                }
                throw;
            }
            if (SR != null)
            {
                SR.Flush();
                SR.Close();
            }

        }
        public static void UpdateSiteQuota(SPSite site, int NewQuota)
        {
            //Convert from MB to Bytes
            long Quota = Convert.ToInt64(NewQuota) * Convert.ToInt64(1024 * 1024);
            site.Quota.StorageMaximumLevel += Quota;
            site.Quota.StorageWarningLevel += Quota;
        }
        public static void PopulatePrincipalPermissions(ISecurableObject SecurableSPObject, SPPrincipal principal, ref DataTable OpenAccessPermissions,
                                                        string Url, string Type, string ID,string GrantedThrough, bool Inherited)
        {
            SPRoleDefinitionBindingCollection SPPermissions = null;
            try
            {
                SPPermissions = SecurableSPObject.RoleAssignments.GetAssignmentByPrincipal(principal).RoleDefinitionBindings;
            }
            catch (ArgumentException)
            {
            }
            if (SPPermissions != null)
                AddSPPermissionsToPermissionsTable(SPPermissions, ref OpenAccessPermissions, Url, Type, ID, Inherited, GrantedThrough);
        }
        public static DataTable GetNtAuthenticatedUsersPermission(SPWeb web,bool CheckLists, bool CheckListItems, bool ReportListssWithUniquePermissionsOnly, bool RemoveSystemLists)
        {
            DataTable OpenAccessPermissions = null;
            if (web == null) return OpenAccessPermissions;
            string WebUrl = web.Url;

            string NTAuthUserName = "NT AUTHORITY\\authenticated users";
            //Check if the user exists in the site collection or not
            if (DoesPrincipalExist(NTAuthUserName, web,PrincipalType.DomainGroup))
            {
                SPUser NTAuthUser = web.AllUsers[NTAuthUserName];
                //Check permissions granted through groups
                foreach (SPGroup group in NTAuthUser.Groups)
                {
                    PopulatePrincipalPermissions(web, group, ref OpenAccessPermissions, WebUrl, "Site", web.ID.ToString(), group.Name , !web.HasUniqueRoleAssignments);
                }
                //Check for direct permissions
                bool WebOpenAccess = web.DoesUserHavePermissions(NTAuthUserName, SPBasePermissions.ViewPages);
                if (WebOpenAccess)
                {
                    PopulatePrincipalPermissions(web, NTAuthUser, ref OpenAccessPermissions, WebUrl, "Site", web.ID.ToString(), "Direct Assignment", !web.HasUniqueRoleAssignments);
                }

                if (CheckLists)
                {
                    foreach (SPList list in web.Lists)
                    {
                        //Remove system lists
                        if (RemoveSystemLists && list.Hidden == true && !list.AllowDeletion)
                            continue;
                        //Skip lists that inherits permissions
                        if (!CheckListItems && !list.HasUniqueRoleAssignments) 
                            continue;
                        else if ((!ReportListssWithUniquePermissionsOnly && !list.HasUniqueRoleAssignments) ||
                                  list.HasUniqueRoleAssignments)
                        {
                            //Check permissions granted through groups
                            foreach (SPGroup group in NTAuthUser.Groups)
                            {
                                PopulatePrincipalPermissions(list, group, ref OpenAccessPermissions, WebUrl + list.DefaultViewUrl, "List", list.ID.ToString(), group.Name, !list.HasUniqueRoleAssignments);
                            }
                            //Check for direct permissions
                            bool ListOpenAccess = list.DoesUserHavePermissions(NTAuthUser, SPBasePermissions.ViewPages);
                            if (ListOpenAccess)
                            {
                                PopulatePrincipalPermissions(list, NTAuthUser, ref OpenAccessPermissions, WebUrl + list.DefaultViewUrl, "List", list.ID.ToString(),"Direct Assignment", !list.HasUniqueRoleAssignments);
                            }
                        }
                        if (CheckListItems)
                        {
                            foreach (SPListItem item in list.Items)//TODO: Might break if you run it in SP2010 and above due to throttling
                            {
                                //Skip list items that don't have unique permissions
                                if (!item.HasUniqueRoleAssignments) continue;
                                //Check permissions granted through groups
                                foreach (SPGroup group in NTAuthUser.Groups)
                                {
                                    PopulatePrincipalPermissions(item, group, ref OpenAccessPermissions, WebUrl + "/" + item.Url, "Item", item.ID.ToString(), group.Name, !item.HasUniqueRoleAssignments);
                                }
                                //Check for direct permissions
                                bool ItemOpenAccess = item.DoesUserHavePermissions(NTAuthUser, SPBasePermissions.ViewPages);
                                if (ItemOpenAccess)
                                {
                                    PopulatePrincipalPermissions(item, NTAuthUser, ref OpenAccessPermissions, WebUrl + "/" + item.Url, "Item", item.ID.ToString(),"Direct Assignment", !item.HasUniqueRoleAssignments);
                                }
                            }
                        }
                    }
                }
               
            }
            return OpenAccessPermissions;
        }
        public static DataTable InitializePermissionsTable()
        {
            DataTable Permissions = new DataTable("Open Access");
            Permissions.Columns.Add("Type");
            Permissions.Columns.Add("ID");
            Permissions.Columns.Add("Url");
            Permissions.Columns.Add("Permission");
            Permissions.Columns.Add("Granted Through");
            Permissions.Columns.Add("Inherited");
            return Permissions;
        }
        private static void AddSPPermissionsToPermissionsTable(SPRoleDefinitionBindingCollection SPPermissions, ref DataTable Permissions,
                                                               string Url, string Type, string ID, bool Inherited,string GrantedThrough)
        {
            if (Permissions == null)
            {
                Permissions = new DataTable("Open Access");
                Permissions = InitializePermissionsTable();
            }
            foreach (SPRoleDefinition perm in SPPermissions)
            {
                DataRow newPermissionRecord = Permissions.NewRow();
                newPermissionRecord["Type"] = Type;
                newPermissionRecord["ID"] = ID;
                newPermissionRecord["Url"] = Url;
                newPermissionRecord["Permission"] = perm.Name;
                newPermissionRecord["Granted Through"] = GrantedThrough;
                newPermissionRecord["Inherited"] = Inherited ? "True" : "False";
                Permissions.Rows.Add(newPermissionRecord);
            }
        }
        public static bool DoesPrincipalExist(string LoginName, SPWeb web,PrincipalType PType)
        {
            SPQuery UsersQuery = new SPQuery();
            //Build the user CAML Query
            string IsNotDeletedCondition = "<Eq><FieldRef Name='Deleted'/>" +
                                            "<Value Type='Boolean'>0</Value></Eq>";
            string PrincipalCodition = "<Eq><FieldRef Name='ContentType'/>"+
                                            "<Value Type='TEXT'>"+PType.ToString()+"</Value></Eq>";
            string IsActiveCondition = "<Eq><FieldRef ID='IsActive'/>" +
                                       "<Value Type='Boolean'>1</Value></Eq>";
            UsersQuery.Query = string.Concat(
                              "<Where><And><And><And>",
                              PrincipalCodition,
                              "<Eq><FieldRef Name='Name'/>" +
                              "<Value Type='TEXT'>",
                              LoginName,
                              "</Value></Eq></And>",
                              IsNotDeletedCondition,
                              "</And>",
                              IsActiveCondition,
                              "</And>",
                           "</Where>",
                           "<OrderBy>",
                              "<FieldRef Name='Name' Ascending='TRUE' />",
                           "</OrderBy>");

            UsersQuery.ViewFields = string.Concat(
                                "<FieldRef Name='Name' /><FieldRef Name='IsActive' /><FieldRef Name='EMail' /><FieldRef Name='Account' />");

            //UsersQuery.ViewFieldsOnly = true; // Fetch only the data that we need.

            SPListItemCollection UsersResult = web.SiteUserInfoList.GetItems(UsersQuery);
            if (UsersResult.Count > 0) return true;

            return false;
        }
        #region Screen Messages Control
        public static void WriteError(string format, params object[] args)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Error: " + format, args);
            Console.ResetColor();
        }
        public static void WriteError(string format, object arg)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Error: " + format, arg);
            Console.ResetColor();
        }
        public static void WriteError(string Message)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Error: " + Message);
            Console.ResetColor();
        }
        public static void WriteWarning(string format, params object[] args)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(format, args);
            Console.ResetColor();
        }
        public static void WriteWarning(string format, object arg)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(format, arg);
            Console.ResetColor();
        }
        public static void WriteWarning(string Message)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(Message);
            Console.ResetColor();
        }
        public static void WriteSuccess(string format, params object[] args)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(format, args);
            Console.ResetColor();
        }
        public static void WriteSuccess(string format, object arg)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(format, arg);
            Console.ResetColor();
        }
        public static void WriteSuccess(string Message)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(Message);
            Console.ResetColor();
        }
        public static void WriteMessage(string Message)
        {
            Console.ResetColor();
            Console.WriteLine(Message);
        }
        #endregion
    }
    public class UserInfoObj
    {
        public UserInfoObj()
        { }
        public UserInfoObj(string loginName, string displayName, string email)
        {
            LoginName = loginName;
            DisplayName = displayName;
            Email = email;
        }
        public UserInfoObj(string loginName, string displayName, string email, HashSet<string> sites)
        {
            LoginName = loginName;
            DisplayName = displayName;
            Email = email;
            Sites = sites;
        }
        public UserInfoObj(string loginName, string displayName, string email, HashSet<string> sites, HashSet<string> webs)
        {
            LoginName = loginName;
            DisplayName = displayName;
            Email = email;
            Sites = sites;
            Webs = webs;
        }
        public string LoginName = "";
        public string DisplayName = "";
        public string Email = "";
        public HashSet<string> Sites = new HashSet<string>();
        public HashSet<string> Webs = new HashSet<string>();

    }
    public enum PrincipalType
    {
        Person,
        DomainGroup
    }
}
